/*
 * jPOS Project [http://jpos.org]
 * Copyright (C) 2000-2015 Alejandro P. Revilla
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.jpos.transaction.gui;

import org.jpos.transaction.TransactionManager;
import org.jpos.transaction.TransactionStatusEvent;
import org.jpos.transaction.TransactionStatusListener;
import org.jpos.ui.UI;
import org.jpos.util.TPS;

import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TMMonitor extends JPanel
		implements TransactionStatusListener, SwingConstants, ActionListener, AncestorListener {
	UI ui;
	TransactionManager txnmgr;
	JTable table;
	AbstractTableModel model;
	TransactionStatusEvent[] events;
	JLabel tps = new JLabel("0");
	JLabel tpsAvg = new JLabel("0.00");
	JLabel tpsPeak = new JLabel("0");
	JLabel inTransit = new JLabel("0");
	JLabel outstanding = new JLabel("0");
	Timer timer;

	static final Font SMALL = Font.decode("terminal-plain-8");
	static final Font LARGE = Font.decode("terminal-plain-18");

	Color[] color = new Color[]{
	    /* READY               */ Color.white,
        /* PREPARING           */ new Color(0xb3, 0xb3, 0xff), // blue
        /* PREPARING_FOR_ABORT */ new Color(0xff, 0xa3, 0xa3), // red
        /* COMMITTING          */ new Color(0xd1, 0xff, 0xd1), // green
        /* ABORTING            */ new Color(0xff, 0xa3, 0xa3), // red
        /* DONE                */ Color.white,
        /* PAUSED              */ new Color(0xff, 0x7f, 0x50)  // orange
	};

	public TMMonitor(UI ui, TransactionManager tmmgr) {
		super();
		this.ui = ui;
		this.txnmgr = tmmgr;
		setLayout(new BorderLayout());
		setBorder(BorderFactory.createRaisedBevelBorder());
		model = createModel();

		table = createTable(model);
		JScrollPane scrollPane = new JScrollPane(table);

		add(createTPSPanel(), BorderLayout.EAST);
		add(scrollPane, BorderLayout.CENTER);
		tmmgr.addListener(this);
		addAncestorListener(this);
	}

	public void update(TransactionStatusEvent e) {
		if (ui.isDestroyed()) {
			return;
		}
		int row = e.getSession();
		events[row] = e;
		model.fireTableRowsUpdated(row, row);
		// table.getSelectionModel().setSelectionInterval(row, row);
		setBackgroundColor(row, color[e.getState().intValue()]);
		inTransit.setText(Long.toString(txnmgr.getInTransit()));
		outstanding.setText(Long.toString(txnmgr.getOutstandingTransactions()));
	}

	private void setBackgroundColor(int row, Color color) {
		for (int i = 0; i < model.getColumnCount(); i++) {
			((DefaultTableCellRenderer) table.getCellRenderer(row, i)).setBackground(color);
		}
	}

	private JTable createTable(TableModel model) {
		JTable table = new JTable(model);
		table.setSurrendersFocusOnKeystroke(true);
		table.setFillsViewportHeight(true);
		table.setShowVerticalLines(true);
		table.setCellSelectionEnabled(false);
		table.setDoubleBuffered(true);
		TableColumnModel tcm = table.getColumnModel();

		tcm.getColumn(0).setPreferredWidth(10);
		tcm.getColumn(1).setPreferredWidth(25);
		tcm.getColumn(2).setPreferredWidth(100);
		tcm.getColumn(3).setPreferredWidth(600);
		return table;
	}

	private JComponent createTPSPanel() {
		JPanel outer = new JPanel(new BorderLayout());

		JPanel p = new JPanel(new GridLayout(5, 2));

		add(p, tps, "TPS");
		add(p, tpsPeak, "Peak TPS");
		add(p, tpsAvg, "Avg TPS");
		add(p, inTransit, "InTransit");
		add(p, outstanding, "Queue");

		JPanel blackFiller = new JPanel();
		outer.add(p, BorderLayout.NORTH);
		blackFiller.setBackground(Color.black);
		outer.add(blackFiller, BorderLayout.CENTER);
		outer.setPreferredSize(new Dimension(170, 0));
		return outer;
	}

	private void add(JPanel p, JLabel l, String description) {
		setLabelStyle(l, LARGE, RIGHT);
		p.add(l);
		p.add(setLabelStyle(new JLabel(description), SMALL, LEFT));
	}

	private JLabel setLabelStyle(JLabel l, Font f, int alignment) {
		l.setBorder(BorderFactory.createLoweredBevelBorder());
		l.setFont(f);
		l.setOpaque(true);
		l.setForeground(Color.green);
		l.setBackground(Color.black);
		// l.setAlignment(alignment);
		l.setHorizontalAlignment(alignment);
		l.setVerticalAlignment(BOTTOM);
		return l;
	}

	private AbstractTableModel createModel() {
		events = new TransactionStatusEvent[txnmgr.getActiveSessions()];
		return new AbstractTableModel() {
			String[] columnName = new String[]{
					"#", "id", "State", "Info"
			};
			Class[] columnClass = new Class[]{
					Integer.class, Long.class, String.class, String.class
			};

			public int getColumnCount() {
				return 4;
			}

			public int getRowCount() {
				return txnmgr.getActiveSessions();
			}

			@Override
			public String getColumnName(int columnIndex) {
				return columnName[columnIndex];
			}

			public Class getColumnClass(int columnIndex) {
				return columnClass[columnIndex];
			}

			public Object getValueAt(int row, int col) {
				if (events[row] != null) {
					switch (col) {
						case 0:
							return row;
						case 1:
							return events[row].getId();
						case 2:
							return events[row].getStateAsString();
						case 3:
							return events[row].getInfo();
					}
				}
				return "";
			}
		};
	}

	public void actionPerformed(ActionEvent e) {
		TPS t = txnmgr.getTPS();
		tps.setText(Integer.toString(t.intValue()));
		tpsAvg.setText(String.format("%.2f", t.getAvg()));
		tpsPeak.setText(Integer.toString(t.getPeak()));
	}

	public void ancestorAdded(AncestorEvent event) {
		if (timer == null) {
			timer = new Timer(1000, this);
			timer.start();
		}
	}

	public void ancestorRemoved(AncestorEvent event) {
		if (timer != null) {
			timer.stop();
			timer = null;
		}
	}

	public void ancestorMoved(AncestorEvent event) {
	}
}
